#region NUnit / MsTest switch

#if !NUNIT
using System.Configuration;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Slf;
using Slf.Config;
using Slf.Factories;
using SLF.Test.Resolver;
using Category = Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute;
#else
using NUnit.Framework;
using TestClass = NUnit.Framework.TestFixtureAttribute;
using TestMethod = NUnit.Framework.TestAttribute;
using TestInitialize = NUnit.Framework.SetUpAttribute;
using TestCleanup = NUnit.Framework.TearDownAttribute;
using TestContext = System.Object;
#endif

#endregion

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace SLF.Test.Config
{
  /// <summary>
  /// 
  /// </summary>
  [TestClass]
  public class Given_AppConfigResolver_When_Parsing_Configuration_Data
  {
    private TestLogger testLogger;

    private AppConfigFactoryResolverTarget CreateResolver(string exeName)
    {
      AppConfigFactoryResolverTarget appConfigResolver = new AppConfigFactoryResolverTarget();

      Configuration config = ConfigurationManager.OpenExeConfiguration(exeName);
      SlfConfigurationSection slfConfig = (SlfConfigurationSection)config.GetSection("slf");

      // load the configuration
      appConfigResolver.LoadConfiguration(slfConfig);
      return appConfigResolver;
    }

    [TestInitialize]
    public void Init()
    {
      testLogger = new TestLogger();
      LoggerService.SetDiagnosticLoggerFactory(new SimpleLoggerFactory(testLogger));
    }

    [TestCleanup]
    public void Cleanup()
    {
      LoggerService.SetDiagnosticLoggerFactory(null);
    }

    [TestMethod]
    [DeploymentItem("TestData/Configurations/NamedLoggerAndFactory.exe")]
    [DeploymentItem("TestData/Configurations/NamedLoggerAndFactory.exe.config")]
    public void Factory_Referenced_By_Logger_Is_Returned()
    {
        AppConfigFactoryResolverTarget resolver = CreateResolver("NamedLoggerAndFactory.exe");
            
        var factory =  resolver.GetFactory("myLogger");
        Assert.IsNotNull(factory);
        Assert.IsInstanceOfType(factory, typeof(ConsoleLoggerFactory));
    }

    [TestMethod]
    [DeploymentItem("TestData/Configurations/NamedLoggerAndFactory.exe")]
    [DeploymentItem("TestData/Configurations/NamedLoggerAndFactory.exe.config")]
    public void Implicit_DefaultLogger_Uses_Implicitly_Created_Default_Factory()
    {
      AppConfigFactoryResolverTarget resolver = CreateResolver("NamedLoggerAndFactory.exe");

      var factory = resolver.GetFactory("");
      Assert.IsNotNull(factory);
      Assert.IsInstanceOfType(factory, typeof(NullLoggerFactory));
    }

    [TestMethod]
    [DeploymentItem("TestData/Configurations/DefaultFactory.exe")]
    [DeploymentItem("TestData/Configurations/DefaultFactory.exe.config")]
    public void Implicit_DefaultLogger_Uses_Explicitly_Created_Default_Factory()
    {
      AppConfigFactoryResolverTarget resolver = CreateResolver("DefaultFactory.exe");

      var factory = resolver.GetFactory("");
      Assert.IsNotNull(factory);
      Assert.IsInstanceOfType(factory, typeof(DebugLoggerFactory));
    }

    [TestMethod]
    [DeploymentItem("TestData/Configurations/DefaultLoggerAndFactory.exe")]
    [DeploymentItem("TestData/Configurations/DefaultLoggerAndFactory.exe.config")]
    public void Explicit_DefaultLogger_Uses_Explicitly_Created_Default_Factory()
    {
      AppConfigFactoryResolverTarget resolver = CreateResolver("DefaultLoggerAndFactory.exe");

      var factory = resolver.GetFactory("");
      Assert.IsNotNull(factory);
      Assert.IsInstanceOfType(factory, typeof(ConsoleLoggerFactory));
    }

    [TestMethod]
    [DeploymentItem("TestData/Configurations/DefaultLogger.exe")]
    [DeploymentItem("TestData/Configurations/DefaultLogger.exe.config")]
    public void Explicit_DefaultLogger_Uses_Implicitly_Created_Default_Factory()
    {
      AppConfigFactoryResolverTarget resolver = CreateResolver("DefaultLogger.exe");

      var factory = resolver.GetFactory("");
      Assert.IsNotNull(factory);
      Assert.IsInstanceOfType(factory, typeof(NullLoggerFactory));
    }


    [TestMethod]
    [DeploymentItem("TestData/Configurations/EmptySection.exe")]
    [DeploymentItem("TestData/Configurations/EmptySection.exe.config")]
    public void Empty_Configuration_Should_Create_Default_Factory_And_Logger()
    {
      AppConfigFactoryResolverTarget resolver = CreateResolver("EmptySection.exe");
      Assert.AreEqual(1, resolver.GetFactories().Count);

      var factory = resolver.GetFactory("");
      Assert.IsNotNull(factory);
      Assert.IsInstanceOfType(factory, typeof(NullLoggerFactory));
    }


    [TestMethod]
    [DeploymentItem("TestData/Configurations/MissingSection.exe")]
    [DeploymentItem("TestData/Configurations/MissingSection.exe.config")]
    public void Missing_Configuration_Should_Create_Default_Factory_And_Logger()
    {
      AppConfigFactoryResolverTarget resolver = CreateResolver("MissingSection.exe");
      Assert.AreEqual(1, resolver.GetFactories().Count);

      var factory = resolver.GetFactory("");
      Assert.IsNotNull(factory);
      Assert.IsInstanceOfType(factory, typeof(NullLoggerFactory));
    }


    [TestMethod]
    [DeploymentItem("TestData/Configurations/LoggersLinkingToDeclaredDefaultFactory.exe")]
    [DeploymentItem("TestData/Configurations/LoggersLinkingToDeclaredDefaultFactory.exe.config")]
    public void Loggers_Without_Factory_Name_Should_Be_Linked_To_Declared_Default_Factory()
    {
      AppConfigFactoryResolverTarget resolver = CreateResolver("LoggersLinkingToDeclaredDefaultFactory.exe");

      //default entries
      var debugFactoryType = typeof(DebugLoggerFactory);
      Assert.IsInstanceOfType(resolver.GetFactory(""), debugFactoryType);
      Assert.IsInstanceOfType(resolver.GetFactory("one"), debugFactoryType);
      Assert.IsInstanceOfType(resolver.GetFactory("two"), debugFactoryType);
      Assert.IsInstanceOfType(resolver.GetFactory("four"), debugFactoryType);

      //name factory reference
      var consoleFactoryType = typeof(ConsoleLoggerFactory);
      Assert.IsInstanceOfType(resolver.GetFactory("three"), consoleFactoryType);
    }

    
    [TestMethod]
    [DeploymentItem("TestData/Configurations/LoggersLinkingToUndeclaredDefaultFactory.exe")]
    [DeploymentItem("TestData/Configurations/LoggersLinkingToUndeclaredDefaultFactory.exe.config")]
    public void Loggers_Without_Factory_Name_Should_Be_Linked_To_Implicit_Default_Factory()
    {
      AppConfigFactoryResolverTarget resolver = CreateResolver("LoggersLinkingToUndeclaredDefaultFactory.exe");


      //defualt entries
      var nullLoggerFactoryType = typeof(NullLoggerFactory);
      Assert.IsInstanceOfType(resolver.GetFactory(""), nullLoggerFactoryType);
      Assert.IsInstanceOfType(resolver.GetFactory("one"), nullLoggerFactoryType);
      Assert.IsInstanceOfType(resolver.GetFactory("two"), nullLoggerFactoryType);
      Assert.IsInstanceOfType(resolver.GetFactory("four"), nullLoggerFactoryType);

      //name factory reference
      var consoleFactoryType = typeof(ConsoleLoggerFactory);
      Assert.IsInstanceOfType(resolver.GetFactory("three"), consoleFactoryType);
    }

    

        [TestMethod]
        [DeploymentItem("TestData/Configurations/LoggersLinkingToDeclaredDefaultFactory.exe")]
        [DeploymentItem("TestData/Configurations/LoggersLinkingToDeclaredDefaultFactory.exe.config")]
        public void Multiple_Logger_Declarations_Pointing_To_Same_Factory_Should_Share_Factory_Instance()
        {
          AppConfigFactoryResolverTarget resolver = CreateResolver("LoggersLinkingToDeclaredDefaultFactory.exe");

          var factory = resolver.GetFactory(LoggerService.DefaultLoggerName);

          //get factories
          Assert.AreSame(factory, resolver.GetFactory(""));
          Assert.AreSame(factory, resolver.GetFactory("one"));
          Assert.AreSame(factory, resolver.GetFactory("two"));
          Assert.AreSame(factory, resolver.GetFactory("four"));
        }
    
        [TestMethod]
        [DeploymentItem("TestData/Configurations/DuplicateFactoryName.exe")]
        [DeploymentItem("TestData/Configurations/DuplicateFactoryName.exe.config")]
        public void Duplicate_Factory_Names_Should_Not_Cause_Errors_But_Log_Warning()
        {
          AppConfigFactoryResolverTarget resolver = CreateResolver("DuplicateFactoryName.exe");

          Assert.AreEqual(1, testLogger.LoggedItems.Count);
          Assert.AreEqual(LogLevel.Warn, testLogger.LoggedItems[0].LogLevel);

          // check that the resolver still works
          Assert.IsInstanceOfType(resolver.GetFactory(""), typeof(NullLoggerFactory));
          Assert.IsInstanceOfType(resolver.GetFactory("FooLogger"), typeof(ConsoleLoggerFactory));
        }

        [TestMethod]
        [DeploymentItem("TestData/Configurations/DuplicateFactoryNameCaseInsensitive.exe")]
        [DeploymentItem("TestData/Configurations/DuplicateFactoryNameCaseInsensitive.exe.config")]
        public void Duplicate_Factory_Names_With_Different_Casing_Should_Not_Cause_Errors_But_Log_Warning()
        {
          AppConfigFactoryResolverTarget resolver = CreateResolver("DuplicateFactoryNameCaseInsensitive.exe");

          Assert.AreEqual(1, testLogger.LoggedItems.Count);
          Assert.AreEqual(LogLevel.Warn, testLogger.LoggedItems[0].LogLevel);

          // check that the resolver still works
          Assert.IsInstanceOfType(resolver.GetFactory(""), typeof(NullLoggerFactory));
          Assert.IsInstanceOfType(resolver.GetFactory("FooLogger"), typeof(ConsoleLoggerFactory));
        }

    
        [TestMethod]
        [DeploymentItem("TestData/Configurations/DuplicateLoggerName.exe")]
        [DeploymentItem("TestData/Configurations/DuplicateLoggerName.exe.config")]
        public void Duplicate_Logger_Names_Should_Not_Cause_Errors_But_Log_Warning()
        {
          AppConfigFactoryResolverTarget resolver = CreateResolver("DuplicateLoggerName.exe");
                    
          Assert.AreEqual(1, testLogger.LoggedItems.Count);
          Assert.AreEqual(LogLevel.Warn, testLogger.LoggedItems[0].LogLevel);
          
          Assert.IsInstanceOfType(resolver.GetFactory("one"), typeof (ConsoleLoggerFactory));
        }

        [TestMethod]
        [DeploymentItem("TestData/Configurations/DuplicateLoggerNameCaseInsensitive.exe")]
        [DeploymentItem("TestData/Configurations/DuplicateLoggerNameCaseInsensitive.exe.config")]
        public void Duplicate_Logger_Names_With_Different_Casing_Should_Not_Cause_Errors_But_Log_Warning()
        {          
          AppConfigFactoryResolverTarget resolver = CreateResolver("DuplicateLoggerNameCaseInsensitive.exe");
                    
          Assert.AreEqual(1, testLogger.LoggedItems.Count);
          Assert.AreEqual(LogLevel.Warn, testLogger.LoggedItems[0].LogLevel);

          Assert.IsInstanceOfType(resolver.GetFactory("one"), typeof(ConsoleLoggerFactory));
        }


        [TestMethod]
        [DeploymentItem("TestData/Configurations/LoggerReferenceToUnknownFactory.exe")]
        [DeploymentItem("TestData/Configurations/LoggerReferenceToUnknownFactory.exe.config")]
        public void Reference_To_Unknown_Factory_Should_Log_Error_And_Resolve_To_NullLoggerFactory()
        {
          AppConfigFactoryResolverTarget resolver = CreateResolver("LoggerReferenceToUnknownFactory.exe");
          TestLogger logger = resolver.TestLogger;
          Assert.AreEqual(1, logger.LoggedItems.Count);
          LogItem item = logger.LoggedItems[0];

          Console.Out.WriteLine(item.Message);
          Assert.AreEqual(LogLevel.Error, item.LogLevel);

          Assert.IsInstanceOfType(resolver.GetFactory("invalidLogger"), typeof (NullLoggerFactory));

          //make everything else resolves just fine
          Assert.IsInstanceOfType(resolver.GetFactory(""), typeof(ConsoleLoggerFactory));
          Assert.IsInstanceOfType(resolver.GetFactory("validLogger"), typeof(DebugLoggerFactory));
        }


        [TestMethod]
        [DeploymentItem("TestData/Configurations/InvalidTypeInFactoryDeclaration.exe")]
        [DeploymentItem("TestData/Configurations/InvalidTypeInFactoryDeclaration.exe.config")]
        public void Factory_Declaration_With_Invalid_Type_Information_Should_Log_Error_And_Fall_Back_To_NullLoggerFactory()
        {
          AppConfigFactoryResolverTarget resolver = CreateResolver("InvalidTypeInFactoryDeclaration.exe");
          TestLogger logger = resolver.TestLogger;
                    
          //request the factory
          var factory = resolver.GetFactory("foo");

          //should be a fallback
          Assert.IsInstanceOfType(factory, typeof (NullLoggerFactory));
      
          //we should have a log entry
          Assert.AreEqual(1, logger.LoggedItems.Count);
          LogItem item = logger.LoggedItems[0];
          Assert.AreEqual(LogLevel.Error, item.LogLevel);
        }
    

        [TestMethod]
        [DeploymentItem("TestData/Configurations/UnsupportedTypeInFactoryDeclaration.exe")]
        [DeploymentItem("TestData/Configurations/UnsupportedTypeInFactoryDeclaration.exe.config")]
        public void Factory_Declaration_With_Non_Factory_Type_Declaration_Should_Log_Error_And_Fall_Back_To_NullLoggerFactory()
        {
          AppConfigFactoryResolverTarget resolver = CreateResolver("UnsupportedTypeInFactoryDeclaration.exe");
          TestLogger logger = resolver.TestLogger;

          //request the factory 
          var factory = resolver.GetFactory("foo");

          //should be a fallback
          Assert.IsInstanceOfType(factory, typeof(NullLoggerFactory));

          //we should have a log entry
          Assert.AreEqual(1, logger.LoggedItems.Count);
          LogItem item = logger.LoggedItems[0];
          Assert.AreEqual(LogLevel.Error, item.LogLevel);
        }



        [TestMethod]
        public void Submitting_Null_Reference_Instead_Of_Section_Creates_Default_Factory_And_Logger()
        {
          AppConfigFactoryResolverTarget appConfigResolver = new AppConfigFactoryResolverTarget();

          // load the configuration
          appConfigResolver.LoadConfiguration(null);

          // check we have default factory
          var factory = appConfigResolver.GetFactory("");
          Assert.IsNotNull(factory);
          Assert.IsInstanceOfType(factory, typeof(NullLoggerFactory));
        }



  }
}
